#include "structMeshFunctions.hpp"
#include "meshTypeFunctions.hpp"
#include <catch2/catch_test_macros.hpp>

using namespace hms;

TEST_CASE("Parent cells"){
	auto testOneLayer = [](const auto& mesh){
		Index ne { mesh.nEdges() };
		auto edgeIndices { ArrayXi::LinSpaced(ne, 0, ne-1) };

		Array2Xi cellIndices { parentCells(mesh, edgeIndices, 1) };

		REQUIRE( (cellIndices.col( 0) == Array2i{ 1, 6}).all() );
		REQUIRE( (cellIndices.col( 1) == Array2i{ 2, 7}).all() );
		REQUIRE( (cellIndices.col( 2) == Array2i{ 3, 8}).all() );
		REQUIRE( (cellIndices.col( 3) == Array2i{ 6,11}).all() );
		REQUIRE( (cellIndices.col( 4) == Array2i{ 7,12}).all() );
		REQUIRE( (cellIndices.col( 5) == Array2i{ 8,13}).all() );
		REQUIRE( (cellIndices.col( 6) == Array2i{11,16}).all() );
		REQUIRE( (cellIndices.col( 7) == Array2i{12,17}).all() );
		REQUIRE( (cellIndices.col( 8) == Array2i{13,18}).all() );
		REQUIRE( (cellIndices.col( 9) == Array2i{16,21}).all() );
		REQUIRE( (cellIndices.col(10) == Array2i{17,22}).all() );
		REQUIRE( (cellIndices.col(11) == Array2i{18,23}).all() );
		REQUIRE( (cellIndices.col(12) == Array2i{21,26}).all() );
		REQUIRE( (cellIndices.col(13) == Array2i{22,27}).all() );
		REQUIRE( (cellIndices.col(14) == Array2i{23,28}).all() );
		REQUIRE( (cellIndices.col(15) == Array2i{ 5, 6}).all() );
		REQUIRE( (cellIndices.col(16) == Array2i{ 6, 7}).all() );
		REQUIRE( (cellIndices.col(17) == Array2i{ 7, 8}).all() );
		REQUIRE( (cellIndices.col(18) == Array2i{ 8, 9}).all() );
		REQUIRE( (cellIndices.col(19) == Array2i{10,11}).all() );
		REQUIRE( (cellIndices.col(20) == Array2i{11,12}).all() );
		REQUIRE( (cellIndices.col(21) == Array2i{12,13}).all() );
		REQUIRE( (cellIndices.col(22) == Array2i{13,14}).all() );
		REQUIRE( (cellIndices.col(23) == Array2i{15,16}).all() );
		REQUIRE( (cellIndices.col(24) == Array2i{16,17}).all() );
		REQUIRE( (cellIndices.col(25) == Array2i{17,18}).all() );
		REQUIRE( (cellIndices.col(26) == Array2i{18,19}).all() );
		REQUIRE( (cellIndices.col(27) == Array2i{20,21}).all() );
		REQUIRE( (cellIndices.col(28) == Array2i{21,22}).all() );
		REQUIRE( (cellIndices.col(29) == Array2i{22,23}).all() );
		REQUIRE( (cellIndices.col(30) == Array2i{23,24}).all() );
	};
	auto testTwoLayers = [](const auto& mesh){
		SECTION("First order neighbours"){
			Index ne { mesh.nEdges() };
			auto edgeIndices { ArrayXi::LinSpaced(ne, 0, ne-1) };
			Array2Xi cellIndices { parentCells(mesh, edgeIndices, 1) };

			REQUIRE( (cellIndices.col( 0) == Array2i{ 9,16}).all() );
			REQUIRE( (cellIndices.col( 1) == Array2i{10,17}).all() );
			REQUIRE( (cellIndices.col( 2) == Array2i{11,18}).all() );
			REQUIRE( (cellIndices.col( 3) == Array2i{16,23}).all() );
			REQUIRE( (cellIndices.col( 4) == Array2i{17,24}).all() );
			REQUIRE( (cellIndices.col( 5) == Array2i{18,25}).all() );
			REQUIRE( (cellIndices.col( 6) == Array2i{23,30}).all() );
			REQUIRE( (cellIndices.col( 7) == Array2i{24,31}).all() );
			REQUIRE( (cellIndices.col( 8) == Array2i{25,32}).all() );
			REQUIRE( (cellIndices.col( 9) == Array2i{30,37}).all() );
			REQUIRE( (cellIndices.col(10) == Array2i{31,38}).all() );
			REQUIRE( (cellIndices.col(11) == Array2i{32,39}).all() );
			REQUIRE( (cellIndices.col(12) == Array2i{37,44}).all() );
			REQUIRE( (cellIndices.col(13) == Array2i{38,45}).all() );
			REQUIRE( (cellIndices.col(14) == Array2i{39,46}).all() );
			REQUIRE( (cellIndices.col(15) == Array2i{15,16}).all() );
			REQUIRE( (cellIndices.col(16) == Array2i{16,17}).all() );
			REQUIRE( (cellIndices.col(17) == Array2i{17,18}).all() );
			REQUIRE( (cellIndices.col(18) == Array2i{18,19}).all() );
			REQUIRE( (cellIndices.col(19) == Array2i{22,23}).all() );
			REQUIRE( (cellIndices.col(20) == Array2i{23,24}).all() );
			REQUIRE( (cellIndices.col(21) == Array2i{24,25}).all() );
			REQUIRE( (cellIndices.col(22) == Array2i{25,26}).all() );
			REQUIRE( (cellIndices.col(23) == Array2i{29,30}).all() );
			REQUIRE( (cellIndices.col(24) == Array2i{30,31}).all() );
			REQUIRE( (cellIndices.col(25) == Array2i{31,32}).all() );
			REQUIRE( (cellIndices.col(26) == Array2i{32,33}).all() );
			REQUIRE( (cellIndices.col(27) == Array2i{36,37}).all() );
			REQUIRE( (cellIndices.col(28) == Array2i{37,38}).all() );
			REQUIRE( (cellIndices.col(29) == Array2i{38,39}).all() );
			REQUIRE( (cellIndices.col(30) == Array2i{39,40}).all() );
		}
		SECTION("Second order neighbours"){
			Index ne { mesh.nEdges() };
			auto edgeIndices { ArrayXi::LinSpaced(ne, 0, ne-1) };
			Array2Xi cellIndices { parentCells(mesh, edgeIndices, 2) };

			REQUIRE( (cellIndices.col( 0) == Array2i{ 2,23}).all() );
			REQUIRE( (cellIndices.col( 1) == Array2i{ 3,24}).all() );
			REQUIRE( (cellIndices.col( 2) == Array2i{ 4,25}).all() );
			REQUIRE( (cellIndices.col( 3) == Array2i{ 9,30}).all() );
			REQUIRE( (cellIndices.col( 4) == Array2i{10,31}).all() );
			REQUIRE( (cellIndices.col( 5) == Array2i{11,32}).all() );
			REQUIRE( (cellIndices.col( 6) == Array2i{16,37}).all() );
			REQUIRE( (cellIndices.col( 7) == Array2i{17,38}).all() );
			REQUIRE( (cellIndices.col( 8) == Array2i{18,39}).all() );
			REQUIRE( (cellIndices.col( 9) == Array2i{23,44}).all() );
			REQUIRE( (cellIndices.col(10) == Array2i{24,45}).all() );
			REQUIRE( (cellIndices.col(11) == Array2i{25,46}).all() );
			REQUIRE( (cellIndices.col(12) == Array2i{30,51}).all() );
			REQUIRE( (cellIndices.col(13) == Array2i{31,52}).all() );
			REQUIRE( (cellIndices.col(14) == Array2i{32,53}).all() );
			REQUIRE( (cellIndices.col(15) == Array2i{14,17}).all() );
			REQUIRE( (cellIndices.col(16) == Array2i{15,18}).all() );
			REQUIRE( (cellIndices.col(17) == Array2i{16,19}).all() );
			REQUIRE( (cellIndices.col(18) == Array2i{17,20}).all() );
			REQUIRE( (cellIndices.col(19) == Array2i{21,24}).all() );
			REQUIRE( (cellIndices.col(20) == Array2i{22,25}).all() );
			REQUIRE( (cellIndices.col(21) == Array2i{23,26}).all() );
			REQUIRE( (cellIndices.col(22) == Array2i{24,27}).all() );
			REQUIRE( (cellIndices.col(23) == Array2i{28,31}).all() );
			REQUIRE( (cellIndices.col(24) == Array2i{29,32}).all() );
			REQUIRE( (cellIndices.col(25) == Array2i{30,33}).all() );
			REQUIRE( (cellIndices.col(26) == Array2i{31,34}).all() );
			REQUIRE( (cellIndices.col(27) == Array2i{35,38}).all() );
			REQUIRE( (cellIndices.col(28) == Array2i{36,39}).all() );
			REQUIRE( (cellIndices.col(29) == Array2i{37,40}).all() );
			REQUIRE( (cellIndices.col(30) == Array2i{38,41}).all() );
		}
	};
	auto mesh { uniMeshAs<UniMesh>(3,4,1,1) };
	testOneLayer(mesh);
}

TEST_CASE("Boundary vertex location"){
	Array2i meshSize {3,4};
	using BL = BoundaryLocation;

	REQUIRE(whereIsVertex( 0, meshSize) == BL::bottom);
	REQUIRE(whereIsVertex( 1, meshSize) == BL::bottom);
	REQUIRE(whereIsVertex( 2, meshSize) == BL::bottom);
	REQUIRE(whereIsVertex( 3, meshSize) == BL::bottom);
	REQUIRE(whereIsVertex( 7, meshSize) == BL::right );
	REQUIRE(whereIsVertex(11, meshSize) == BL::right );
	REQUIRE(whereIsVertex(15, meshSize) == BL::right );
	REQUIRE(whereIsVertex(19, meshSize) == BL::top   );
	REQUIRE(whereIsVertex(18, meshSize) == BL::top   );
	REQUIRE(whereIsVertex(17, meshSize) == BL::top   );
	REQUIRE(whereIsVertex(16, meshSize) == BL::top   );
	REQUIRE(whereIsVertex(12, meshSize) == BL::left  );
	REQUIRE(whereIsVertex( 8, meshSize) == BL::left  );
	REQUIRE(whereIsVertex( 4, meshSize) == BL::left  );

	REQUIRE_THROWS( whereIsVertex( 5, meshSize) );
	REQUIRE_THROWS( whereIsVertex( 6, meshSize) );
	REQUIRE_THROWS( whereIsVertex( 9, meshSize) );
	REQUIRE_THROWS( whereIsVertex(10, meshSize) );
	REQUIRE_THROWS( whereIsVertex(13, meshSize) );
	REQUIRE_THROWS( whereIsVertex(14, meshSize) );
}

TEST_CASE("Next boundary vertex"){
	Array2i meshSize {3,4};

	REQUIRE(nextBoundaryVertex( 0, true, meshSize) ==  4);
	REQUIRE(nextBoundaryVertex( 1, true, meshSize) ==  0);
	REQUIRE(nextBoundaryVertex( 2, true, meshSize) ==  1);
	REQUIRE(nextBoundaryVertex( 3, true, meshSize) ==  2);
	REQUIRE(nextBoundaryVertex( 7, true, meshSize) ==  3);
	REQUIRE(nextBoundaryVertex(11, true, meshSize) ==  7);
	REQUIRE(nextBoundaryVertex(15, true, meshSize) == 11);
	REQUIRE(nextBoundaryVertex(19, true, meshSize) == 15);
	REQUIRE(nextBoundaryVertex(18, true, meshSize) == 19);
	REQUIRE(nextBoundaryVertex(17, true, meshSize) == 18);
	REQUIRE(nextBoundaryVertex(16, true, meshSize) == 17);
	REQUIRE(nextBoundaryVertex(12, true, meshSize) == 16);
	REQUIRE(nextBoundaryVertex( 8, true, meshSize) == 12);
	REQUIRE(nextBoundaryVertex( 4, true, meshSize) ==  8);

	REQUIRE_THROWS( nextBoundaryVertex( 5, true, meshSize) );
	REQUIRE_THROWS( nextBoundaryVertex( 6, true, meshSize) );
	REQUIRE_THROWS( nextBoundaryVertex( 9, true, meshSize) );
	REQUIRE_THROWS( nextBoundaryVertex(10, true, meshSize) );
	REQUIRE_THROWS( nextBoundaryVertex(13, true, meshSize) );
	REQUIRE_THROWS( nextBoundaryVertex(14, true, meshSize) );

	REQUIRE(nextBoundaryVertex( 0, false, meshSize) ==  1);
	REQUIRE(nextBoundaryVertex( 1, false, meshSize) ==  2);
	REQUIRE(nextBoundaryVertex( 2, false, meshSize) ==  3);
	REQUIRE(nextBoundaryVertex( 3, false, meshSize) ==  7);
	REQUIRE(nextBoundaryVertex( 7, false, meshSize) == 11);
	REQUIRE(nextBoundaryVertex(11, false, meshSize) == 15);
	REQUIRE(nextBoundaryVertex(15, false, meshSize) == 19);
	REQUIRE(nextBoundaryVertex(19, false, meshSize) == 18);
	REQUIRE(nextBoundaryVertex(18, false, meshSize) == 17);
	REQUIRE(nextBoundaryVertex(17, false, meshSize) == 16);
	REQUIRE(nextBoundaryVertex(16, false, meshSize) == 12);
	REQUIRE(nextBoundaryVertex(12, false, meshSize) ==  8);
	REQUIRE(nextBoundaryVertex( 8, false, meshSize) ==  4);
	REQUIRE(nextBoundaryVertex( 4, false, meshSize) ==  0);

	REQUIRE_THROWS( nextBoundaryVertex( 5, false, meshSize) );
	REQUIRE_THROWS( nextBoundaryVertex( 6, false, meshSize) );
	REQUIRE_THROWS( nextBoundaryVertex( 9, false, meshSize) );
	REQUIRE_THROWS( nextBoundaryVertex(10, false, meshSize) );
	REQUIRE_THROWS( nextBoundaryVertex(13, false, meshSize) );
	REQUIRE_THROWS( nextBoundaryVertex(14, false, meshSize) );
}